package report2.methods;

import report2.classes.MyProcess;

import java.util.*;

/**
 * Author：zouran
 * Date：2023/4/20  15:28
 * Description:进程方式
 */
public class Calculate {
    //记录当前全部进程
    private final List<MyProcess> myProcessList = new ArrayList<>();
    //记录进程输出顺序
    private final List<String> nameList = new ArrayList<>();
    //时间片队列
    LinkedList<MyProcess> myProcessQueue = new LinkedList<>();
    //记录当前时间，时间
    private Integer currentTime = 0;

    //无参构造代替初始化
    public Calculate() {
        char[][] list = {
                {'A', '0', '4', '4'},
                {'B', '1', '3', '2'},
                {'C', '2', '5', '3'},
                {'D', '3', '2', '5'},
                {'E', '4', '4', '1'},};
        for (char[] chars : list) {
            MyProcess myProcess = new MyProcess();
            myProcess.setName(String.valueOf(chars[0]));
            myProcess.setArrival((int) chars[1] - 48);
            myProcess.setService((int) chars[2] - 48);
            myProcess.setWeight((int) chars[3] - 48);
            this.myProcessList.add(myProcess);
        }
        //按到达时间排序
        myProcessList.sort(Comparator.comparing(MyProcess::getArrival));
    }

    //获取当前未完成的进程中最先到达进程,返回索引位置,返回-1代表进程全部结束
    public int findFirstIndex() {
        int index = -1, min = Integer.MAX_VALUE;
        for (int i = 0; i < myProcessList.size(); i++) {
            //进程未完成且最先到
            if (!myProcessList.get(i).isFinish()
                    && myProcessList.get(i).getArrival() <= min
                    && myProcessList.get(i).getArrival() <= currentTime) {
                min = myProcessList.get(i).getArrival();
                index = i;
            }
        }
        return index;
    }

    //获取当前未完成的进程中最短作业进程,返回索引位置,返回-1代表进程全部结束
    public int findShortIndex() {
        int index = -1, min = Integer.MAX_VALUE;
        for (int i = 0; i < myProcessList.size(); i++) {
            //进程未完成且最先到
            if (!myProcessList.get(i).isFinish()
                    && myProcessList.get(i).getService() <= min
                    && myProcessList.get(i).getArrival() <= currentTime) {
                min = myProcessList.get(i).getService();
                index = i;
            }
        }
        return index;
    }

    //原理同短作业进程
    public int findWeightIndex() {
        int index = -1, min = Integer.MAX_VALUE;
        for (int i = 0; i < myProcessList.size(); i++) {
            //进程未完成且最先到
            if (!myProcessList.get(i).isFinish()
                    && myProcessList.get(i).getWeight() <= min
                    && myProcessList.get(i).getArrival() <= currentTime) {
                min = myProcessList.get(i).getWeight();
                index = i;
            }
        }
        return index;
    }

    //添加进程到进程队列
    public void findTimeIndex(Integer choice) {
        for (MyProcess myProcess : myProcessList) {
            boolean isNotInQueue = true;
            //当前时间内到达的进程
            if (myProcess.getArrival() <= currentTime)
            //遍历进程队列
            {
                for (MyProcess process : myProcessQueue) {
                    //若存在，则标记为false
                    if (Objects.equals(myProcess.getName(), process.getName())) {
                        isNotInQueue = false;
                        break;
                    }
                }
                if (isNotInQueue) {
                    if (choice == 1)
                        myProcessQueue.addFirst(myProcess);
                    else if (choice == 2)
                        myProcessQueue.addLast(myProcess);
                }
            }
        }
    }

    //更新进程状态
    public void updateMyProcessList(MyProcess myProcess) {
        for (MyProcess process : myProcessList) {
            if (Objects.equals(process.getName(), myProcess.getName())) {
                process.setStart(myProcess.getStart());
                process.setEnd(myProcess.getEnd());
                process.setCurrent(myProcess.getCurrent());
                break;
            }
        }
    }

    //非抢占式方法中进程运行参数的变化是一致的
    public void commonSearch(int index) {
        //index=-1有俩种情况:1.所有作业完成(外层循环条件会判断) 2.存在时间段空作业
        if (index != -1) {
            int i = 1;
            //设置进程开始使时间
            myProcessList.get(index).setStart(currentTime);
            while (i <= myProcessList.get(index).getService()) {
                //记录进程
                nameList.add(myProcessList.get(index).getName());
                //更新当前时间
                currentTime++;
                //进程完成度+1
                myProcessList.get(index).setCurrent(myProcessList.get(index).getCurrent() + 1);
                i++;
            }
            //记录结束时间
            myProcessList.get(index).setEnd(currentTime);
        } else {
            //当存在时间段空作业时
            currentTime++;
        }
    }

    //判断是否所有的进程已完成
    public boolean isNotAllFinished() {
        for (MyProcess myProcess : myProcessList) {
            if (!myProcess.isFinish()) return true;
        }
        return false;
    }

    //先来先到算法
    public void FCFS() {
        //循环结束条件为所有进程均完成
        while (isNotAllFinished()) {
            //获取当前未完成的进程中最先到达进程
            int index = findFirstIndex();
            commonSearch(index);
        }
        print("FCFS");
    }

    //短进程优先算法
    public void SJPF() {
        while (isNotAllFinished()) {
            int index = findShortIndex();
            commonSearch(index);
        }
        print("SJPF");
    }

    public void method3() {
        System.out.println("请进一步选择（0-非抢占优先权算法；1-抢占优先权算法）");
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();
        if (choice == 0) method3_1();
        else if (choice == 1) method3_2();
    }

    //0-非抢占优先权算法
    public void method3_1() {
        while (isNotAllFinished()) {
            int index = findWeightIndex();
            commonSearch(index);
        }
        print("非抢占优先权算法");
    }

    //1-抢占优先权算法
    public void method3_2() {
        while (isNotAllFinished()) {
            int index = findWeightIndex();
            //index=-1有俩种情况:1.所有作业完成(外层循环条件会判断) 2.存在时间段空作业
            if (index != -1) {
                //设置进程开始使时间
                myProcessList.get(index).setStart(currentTime);
                //记录进程
                nameList.add(myProcessList.get(index).getName());
                //更新当前时间
                currentTime++;
                //进程完成度+1
                myProcessList.get(index).setCurrent(myProcessList.get(index).getCurrent() + 1);
                //记录结束时间
                if (myProcessList.get(index).isFinish()) myProcessList.get(index).setEnd(currentTime);
            } else {
                //当存在时间段空作业时
                currentTime++;
            }
        }
        print("抢占优先权算法");
    }

    public void method4() {
        System.out.println("请进一步选择时间片大小:Q=1到3区间的整数");
        Scanner scanner = new Scanner(System.in);
        int week = scanner.nextInt();
        System.out.println("请进一步选择进程方式:0-新进程优先,1-老进程优先");
        int choice = scanner.nextInt();
        if (choice == 0)
            method4_1(week, 1);
        else if (choice == 1)
            method4_1(week, 2);
    }

    //时间片轮转算法
    public void method4_1(Integer week, Integer choice) {
        while (isNotAllFinished()) {
            findTimeIndex(choice);
            if (myProcessQueue.isEmpty())
                continue;
            MyProcess currentProcess = myProcessQueue.poll();
            //若进程未开始，设置进程开始使时间
            if (currentProcess.getCurrent() == 0)
                currentProcess.setStart(currentTime);
            //周期week内进程未完成
            if (currentProcess.getService() - currentProcess.getCurrent() >= week)
            //进程完成度+week
            {
                //以每秒为单位记录进程
                for (int i = 1; i <= week; i++) {
                    nameList.add(currentProcess.getName());
                }
                //更新进程状态
                currentProcess.setCurrent(currentProcess.getCurrent() + week);
                //更新当前时间
                currentTime += week;
            } else {
                //以每秒为单位记录进程
                for (int i = 1; i <= currentProcess.getService() - currentProcess.getCurrent(); i++) {
                    nameList.add(currentProcess.getName());
                }
                //更新当前时间
                currentTime += currentProcess.getService() - currentProcess.getCurrent();
                //该周期内能完成进程，更新进程状态
                currentProcess.setCurrent(currentProcess.getService());
            }
            //记录结束时间
            if (currentProcess.isFinish() && currentProcess.getEnd() == 0) currentProcess.setEnd(currentTime);
                //未完成则加入队尾
            else myProcessQueue.add(currentProcess);
            //更新总进程队列中currentProcess对应进程信息
            updateMyProcessList(currentProcess);
        }
        if (choice == 1)
            print("时间片轮转算法(新进程优先)");
        else if (choice == 2) print("时间片轮转算法(老进程优先)");
    }

    //计算平均周转周期
    public double getAvgPeriod1() {
        double sum = 0.0;
        for (MyProcess myProcess : myProcessList) {
            sum += myProcess.getPeriod1();
        }
        return sum / myProcessList.size();
    }

    //计算平均带权周转周期
    public double getAvgPeriod2() {
        double sum = 0.0;
        for (MyProcess myProcess : myProcessList) {
            sum += myProcess.getPeriod2();
        }
        return sum / myProcessList.size();
    }

    //格式化输出
    public void print(String name) {
        System.out.printf("\t\t\t\t%s\n", name);
        System.out.printf("\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "进程名", "到达时间", "服务时间", "开始时间", "完成时间", "周转时间", "带权周转时间");
        for (MyProcess myProcess : myProcessList) {
            myProcess.print();
        }
        System.out.printf("\t%s\t\t\t\t\t%f\t%f\n", "平均", getAvgPeriod1(), getAvgPeriod2());
        System.out.print("\t");
        for (String s : nameList) {
            System.out.print("[" + s + "] ");
        }
        System.out.println();
    }
}